/*!	 dock_toolbox.cpp
**	 writeme
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**  Copyright (c) 2008 Paul Wise
**	Copyright (c) 2009 Nikita Kitaev
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
** === N O T E S ===========================================================
**
** ========================================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>

#include <sstream>

#include <gtk/gtk.h>
#include <gtkmm/accelmap.h>

#include "localization.h"

#include "dock_toolbox.h"

#include "instance.h"
#include "app.h"
#include "canvasview.h"
#include "dialogs/dialog_gradient.h"
#include "dialogs/dialog_color.h"
#include "docks/dialog_tooloptions.h"
#include "docks/dockable.h"
#include "docks/dockmanager.h"
#include "docks/dockdialog.h"
#include "widgets/widget_defaults.h"

#include <synfigapp/main.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;
using namespace studio;
using namespace sigc;

Dock_Toolbox::Dock_Toolbox():
    Dockable("toolbox", _("Toolbox"), Gtk::StockID("synfig-about"))
{
    set_use_scrolled(false);
    set_size_request(-1, -1);

    tool_item_group = manage(new class Gtk::ToolItemGroup());
    gtk_tool_item_group_set_label(tool_item_group->gobj(), NULL);

    Gtk::ToolPalette *palette = manage(new Gtk::ToolPalette());
    palette->add(*tool_item_group);
    palette->set_expand(*tool_item_group);
    palette->set_exclusive(*tool_item_group, true);
    palette->set_icon_size(Gtk::IconSize::from_name("synfig-small_icon_16x16"));
    // let the palette propagate the scroll events
    palette->add_events(Gdk::SCROLL_MASK);
    palette->show();

    Gtk::ScrolledWindow *scrolled_window = manage(new Gtk::ScrolledWindow());
    scrolled_window->add(*palette);
    scrolled_window->set_border_width(2);
    scrolled_window->show();

    separator = manage(new class Gtk::HSeparator());

    Widget_Defaults* widget_defaults(manage(new Widget_Defaults()));

    // pack tools and default widgets


    Gtk::VPaned *tool_box_paned = manage(new class Gtk::VPaned());
    tool_box_paned->pack1(*scrolled_window, Gtk::PACK_EXPAND_WIDGET | Gtk::PACK_SHRINK, 3);
    tool_box_paned->pack2(*widget_defaults, Gtk::PACK_EXPAND_WIDGET | Gtk::PACK_SHRINK, 3);
    tool_box_paned->set_position(200);
    tool_box_paned->show_all();
    add(*tool_box_paned);

    App::signal_instance_selected().connect(
        sigc::hide(
            sigc::mem_fun(*this, &studio::Dock_Toolbox::update_tools)
        )
    );

    std::vector<Gtk::TargetEntry> listTargets;
    listTargets.push_back(Gtk::TargetEntry("text/plain"));
    listTargets.push_back(Gtk::TargetEntry("image"));

    drag_dest_set(listTargets);
    signal_drag_data_received().connect(sigc::mem_fun(*this, &studio::Dock_Toolbox::on_drop_drag_data_received));

    changing_state_ = false;

    App::signal_present_all().connect(sigc::mem_fun0(*this, &Dock_Toolbox::present));
}

Dock_Toolbox::~Dock_Toolbox()
{
    hide();
    // studio::App::cb.task(_("Toolbox: I was nailed!"));

    if (studio::App::dock_toolbox == this) {
        studio::App::dock_toolbox = NULL;
    }
}

void
Dock_Toolbox::set_active_state(const synfig::String& statename)
{
    std::map<synfig::String, Gtk::ToggleToolButton *>::iterator iter;

    changing_state_ = true;

    synfigapp::Main::set_state(statename);

    try {

        for (iter = state_button_map.begin(); iter != state_button_map.end(); ++iter) {
            if (iter->first == statename) {
                if (!iter->second->get_active()) {
                    iter->second->set_active(true);
                }
            } else {
                if (iter->second->get_active()) {
                    iter->second->set_active(false);
                }
            }
        }
    } catch (...) {
        changing_state_ = false;
        throw;
    }

    changing_state_ = false;
}

void
Dock_Toolbox::change_state(const synfig::String& statename, bool force)
{
    etl::handle<studio::CanvasView> canvas_view(studio::App::get_selected_canvas_view());

    if (canvas_view) {
        if (!force && statename == canvas_view->get_smach().get_state_name()) {
            return;
        }

        if (state_button_map.count(statename)) {
            state_button_map[statename]->activate();
        } else {
            synfig::error("Unknown state \"%s\"", statename.c_str());
        }
    }
}

void
Dock_Toolbox::change_state_(const Smach::state_base *state)
{
    if (changing_state_) {
        return;
    }

    changing_state_ = true;

    try {
        etl::handle<studio::CanvasView> canvas_view(studio::App::get_selected_canvas_view());

        if (canvas_view) {
            canvas_view->get_smach().enter(state);
        } else {
            refresh();
        }
    } catch (...) {
        changing_state_ = false;
        throw;
    }

    changing_state_ = false;
}

/*! \fn Dock_Toolbox::add_state(const Smach::state_base *state)
 *   Add and connect a toogle button to the toolbox defined by a state
 *  \param state a const pointer to Smach::state_base
*/
void
Dock_Toolbox::add_state(const Smach::state_base *state)
{
    Gtk::Image *icon;

    assert(state);

    String name = state->get_name();

    Gtk::StockItem stock_item;
    Gtk::Stock::lookup(Gtk::StockID("synfig-" + name), stock_item);

    Gtk::ToggleToolButton *tool_button = manage(new class Gtk::ToggleToolButton(
            *manage(new Gtk::Image(
                        stock_item.get_stock_id(),
                        Gtk::IconSize::from_name("synfig-small_icon_16x16"))),
            stock_item.get_label()));

    Gtk::AccelKey key;
    // Have a look to global fonction init_ui_manager() from app.cpp for "accel_path" definition
    Gtk::AccelMap::lookup_entry("<Actions>/action_group_state_manager/state-" + name, key);
    // Gets the, is exist, accelerator representation for labels
    Glib::ustring accel_path = !key.is_null() ? key.get_abbrev() : "";

    tool_button->set_tooltip_text(stock_item.get_label() + " " + accel_path);
    tool_button->show();

    tool_item_group->insert(*tool_button);
    tool_item_group->show_all();

    state_button_map[name] = tool_button;

    tool_button->signal_clicked().connect(
        sigc::bind(
            sigc::mem_fun(*this, &studio::Dock_Toolbox::change_state_),
            state
        )
    );

    refresh();
}

void
Dock_Toolbox::update_tools()
{
    etl::handle<Instance> instance = App::get_selected_instance();
    etl::handle<CanvasView> canvas_view = App::get_selected_canvas_view();

    // These next several lines just adjust the tool buttons
    // so that they are only clickable when they should be.
    if (instance && canvas_view) {
        std::map<synfig::String, Gtk::ToggleToolButton *>::iterator iter;

        for (iter = state_button_map.begin(); iter != state_button_map.end(); ++iter) {
            iter->second->set_sensitive(true);
        }
    } else {
        std::map<synfig::String, Gtk::ToggleToolButton *>::iterator iter;

        for (iter = state_button_map.begin(); iter != state_button_map.end(); ++iter) {
            iter->second->set_sensitive(false);
        }
    }

    if (canvas_view && canvas_view->get_smach().get_state_name()) {
        set_active_state(canvas_view->get_smach().get_state_name());
    } else {
        set_active_state("none");
    }

}

void
Dock_Toolbox::on_drop_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int /*x*/, int /*y*/, const Gtk::SelectionData& selection_data_, guint /*info*/, guint time)
{
    // We will make this true once we have a solid drop
    bool success(false);

    if ((selection_data_.get_length() >= 0) && (selection_data_.get_format() == 8)) {
        synfig::String selection_data((gchar *)(selection_data_.get_data()));

        // For some reason, GTK hands us a list of URLs separated
        // by not only Carriage-Returns, but also Line-Feeds.
        // Line-Feeds will mess us up. Remove all the line-feeds.
        while (selection_data.find_first_of('\r') != synfig::String::npos) {
            selection_data.erase(selection_data.begin() + selection_data.find_first_of('\r'));
        }

        std::stringstream stream(selection_data);

        while (stream) {
            synfig::String filename, URI;
            getline(stream, filename);

            // If we don't have a filename, move on.
            if (filename.empty()) {
                continue;
            }

            // Make sure this URL is of the "file:// " type.
            URI = String(filename.begin(), filename.begin() + sizeof("file://") - 1);

            if (URI != "file://") {
                synfig::warning("Unknown URI (%s) in \"%s\"", URI.c_str(), filename.c_str());
                continue;
            }

            // Strip the "file:// " part from the filename
            filename = synfig::String(filename.begin() + sizeof("file://") - 1, filename.end());

            synfig::info("Attempting to open " + filename);

            if (App::open(filename)) {
                success = true;
            } else {
                synfig::error("Drop failed: Unable to open " + filename);
            }
        }
    } else {
        synfig::error("Drop failed: bad selection data");
    }

    // Finish the drag
    context->drag_finish(success, false, time);
}